[biquadratic~] biquad filter
Filters a signal and calculates parameters using signal or float input
Inlet 1: signal
Inlet 2: center frequency (Hz)
Inlet 3: quality
Inlet 4: decibel gain (peaking/shelf only)
Outlet: filtered signal
Arguments: shape hz q db
Messages: shape, clear
|
Download
biquadratic~.zip - object, help patch, source code
Source Code
//------------------------------------------------------------------------------
// Biquad Filter for Pd
//
// biquadratic~.c
//
// Filters signal through a general purpose biquad
//
// Created by Cooper on 10/16/15.
// Copyright (c) 2015 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
// todo
// add 'clear' message
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// utility header for Pd Spectral Toolkit project
#include "utility.h"
// included for float min
#include <limits.h>
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
//------------------------------------------------------------------------------
// biquadratic_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* biquadratic_class;
static t_class* biquadratic_arg_class;
//------------------------------------------------------------------------------
// filter_shape enum - filter shape names
//------------------------------------------------------------------------------
typedef enum
{
LOWPASS,
HIGHPASS,
BANDPASS,
NOTCH,
ALLPASS,
PEAKING,
LOWSHELF,
HIGHSHELF
}
filter_shape;
//------------------------------------------------------------------------------
// biquadratic - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct biquadratic
{
// this object - must always be first variable in struct
t_object object;
// needed for CLASS_MAINSIGNALIN macro call in biquadratic_tilde_setup
t_float inlet_1;
// floats to hold inlets' values
t_float frequency;
t_float quality;
t_float decibels;
// coefficients
double a1, a2, b0, b1, b2;
// filter sample memory
double x1, x2, y1, y2;
// the filter shape
filter_shape shape;
t_float sample_rate;
} t_biquadratic;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static void biquadratic_lowpass ( t_biquadratic* object );
static void biquadratic_highpass ( t_biquadratic* object );
static void biquadratic_bandpass ( t_biquadratic* object );
static void biquadratic_allpass ( t_biquadratic* object );
static void biquadratic_notch ( t_biquadratic* object );
static void biquadratic_peaking ( t_biquadratic* object );
static void biquadratic_lowshelf ( t_biquadratic* object );
static void biquadratic_highshelf ( t_biquadratic* object );
static void biquadratic_clear ( t_biquadratic* object );
static inline void biquadratic_calc ( t_biquadratic* object );
static t_int* biquadratic_perform ( t_int* io );
static t_int* biquadratic_arg_perform ( t_int* io );
static void biquadratic_dsp ( t_biquadratic* object, t_signal** sig );
static void biquadratic_arg_dsp ( t_biquadratic* object, t_signal** sig );
static void biquadratic_shape ( t_biquadratic* object, t_atom name );
static void* biquadratic_new ( t_symbol* selector, t_int items, t_atom* list );
void biquadratic_tilde_setup ( void );
//------------------------------------------------------------------------------
// biquadratic_lowpass - sets filter shape to lowpass
//------------------------------------------------------------------------------
static void biquadratic_lowpass( t_biquadratic* object )
{
object->shape = LOWPASS;
}
//------------------------------------------------------------------------------
// biquadratic_highpass - sets filter shape to highpass
//------------------------------------------------------------------------------
static void biquadratic_highpass( t_biquadratic* object )
{
object->shape = HIGHPASS;
}
//------------------------------------------------------------------------------
// biquadratic_bandpass - sets filter shape to bandpass
//------------------------------------------------------------------------------
static void biquadratic_bandpass( t_biquadratic* object )
{
object->shape = BANDPASS;
}
//------------------------------------------------------------------------------
// biquadratic_allpass - sets filter shape to allpass
//------------------------------------------------------------------------------
static void biquadratic_allpass( t_biquadratic* object )
{
object->shape = ALLPASS;
}
//------------------------------------------------------------------------------
// biquadratic_notch - sets filter shape to notch
//------------------------------------------------------------------------------
static void biquadratic_notch( t_biquadratic* object )
{
object->shape = NOTCH;
}
//------------------------------------------------------------------------------
// biquadratic_peaking - sets filter shape to peaking
//------------------------------------------------------------------------------
static void biquadratic_peaking( t_biquadratic* object )
{
object->shape = PEAKING;
}
//------------------------------------------------------------------------------
// biquadratic_lowshelf - sets filter shape to lowshelf
//------------------------------------------------------------------------------
static void biquadratic_lowshelf( t_biquadratic* object )
{
object->shape = LOWSHELF;
}
//------------------------------------------------------------------------------
// biquadratic_highshelf - sets filter shape to highshelf
//------------------------------------------------------------------------------
static void biquadratic_highshelf( t_biquadratic* object )
{
object->shape = HIGHSHELF;
}
//------------------------------------------------------------------------------
// biquadratic_clear - clears filter memory
//------------------------------------------------------------------------------
static void biquadratic_clear( t_biquadratic* object )
{
object->x1 = 0;
object->x2 = 0;
object->y1 = 0;
object->y2 = 0;
}
//------------------------------------------------------------------------------
// biquadratic_calc - calculates the filter coefficients
//
// Adapted From:
// Cookbook formulae for audio EQ biquad filter coefficients
// By Robert Bristow-Johnson
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
//
//------------------------------------------------------------------------------
static inline void biquadratic_calc( t_biquadratic* object )
{
// intermediate variables
double quality = ClipMin( object->quality, C_FLOAT_MIN );
double omega = C_2_PI * Clip( object->frequency, C_FLOAT_MIN, object->sample_rate * 0.5 ) / object->sample_rate;
double cos_omega = Cosine( omega );
double alpha = Sine( omega ) / ( 2.0 * quality );
double amp, beta;
double a0, a1, a2, b0, b1, b2;
// calculate based on filter shape
switch( object->shape )
{
case LOWPASS : b0 = ( 1.0 - cos_omega ) * 0.5 ;
b1 = 1.0 - cos_omega ;
b2 = ( 1.0 - cos_omega ) * 0.5 ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case HIGHPASS : b0 = ( 1.0 + cos_omega ) * 0.5 ;
b1 = -( 1.0 + cos_omega ) ;
b2 = ( 1.0 + cos_omega ) * 0.5 ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case BANDPASS : b0 = alpha * quality ;
b1 = 0.0 ;
b2 = -alpha * quality ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case ALLPASS : b0 = 1.0 - alpha ;
b1 = -2.0 * cos_omega ;
b2 = 1.0 + alpha ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case NOTCH : b0 = 1.0 ;
b1 = -2.0 * cos_omega ;
b2 = 1.0 ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case PEAKING : amp = SquareRoot( Power( 10.0, ( object->decibels * 0.05 ) ) );
b0 = 1.0 + ( alpha * amp ) ;
b1 = -2.0 * cos_omega ;
b2 = 1.0 - ( alpha * amp ) ;
a0 = 1.0 + ( alpha / amp ) ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - ( alpha / amp ) ;
break;
case LOWSHELF : amp = SquareRoot( Power( 10.0, ( object->decibels * 0.05 ) ) );
beta = 2.0 * SquareRoot( amp ) * alpha;
b0 = amp * ( ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega + beta ) ;
b1 = 2.0 * amp * ( ( amp - 1.0 ) - ( amp + 1.0 ) * cos_omega ) ;
b2 = amp * ( ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega - beta ) ;
a0 = ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega + beta ;
a1 = -2.0 * ( ( amp - 1.0 ) + ( amp + 1.0 ) * cos_omega ) ;
a2 = ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega - beta ;
break;
case HIGHSHELF : amp = SquareRoot( Power( 10.0, ( object->decibels * 0.05 ) ) );
beta = 2.0 * SquareRoot( amp ) * alpha;
b0 = amp * ( ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega + beta ) ;
b1 = -2.0 * amp * ( ( amp - 1.0 ) + ( amp + 1.0 ) * cos_omega ) ;
b2 = amp * ( ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega - beta ) ;
a0 = ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega + beta ;
a1 = 2.0 * ( ( amp - 1.0 ) - ( amp + 1.0 ) * cos_omega ) ;
a2 = ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega - beta ;
break;
}
// normalize coefficients
object->b0 = b0 / a0;
object->b1 = b1 / a0;
object->b2 = b2 / a0;
object->a1 = a1 / a0;
object->a2 = a2 / a0;
}
//------------------------------------------------------------------------------
// biquadratic_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* biquadratic_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in1 = ( t_float* )( io[ 1 ] );
t_float* in2 = ( t_float* )( io[ 2 ] );
t_float* in3 = ( t_float* )( io[ 3 ] );
t_float* in4 = ( t_float* )( io[ 4 ] );
t_float* out = ( t_float* )( io[ 5 ] );
t_int frames = ( t_int )( io[ 6 ] );
t_biquadratic* o = ( t_biquadratic* )( io[ 7 ] );
// sample i/o temp
double x, y;
// signal vector iterator variable
t_int n = -1;
// the dsp loop
while( ++n < frames )
{
// store the input sample
x = in1[ n ];
// store the parameters
o->frequency = in2[ n ];
o->quality = in3[ n ];
o->decibels = in4[ n ];
// calculate coefficients
biquadratic_calc( o );
// perform direct form I biquad calculation
y = o->b0 * x + o->b1 * o->x1 + o->b2 * o->x2 - o->a1 * o->y1 - o->a2 * o->y2;
// propagate the filter samples
o->x2 = o->x1;
o->x1 = x;
o->y2 = o->y1;
o->y1 = y;
// output the filtered sample
out[ n ] = y;
}
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 8 ] );
}
//------------------------------------------------------------------------------
// biquadratic_arg_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* biquadratic_arg_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in = ( t_float* )( io[ 1 ] );
t_float* out = ( t_float* )( io[ 2 ] );
t_int frames = ( t_int )( io[ 3 ] );
t_biquadratic* o = ( t_biquadratic* )( io[ 4 ] );
// signal vector iterator variable
t_int n = -1;
// sample i/o temp
double x, y;
// calculate coefficients
biquadratic_calc( o );
// the dsp loop
while( ++n < frames )
{
// store the input sample
x = in[ n ];
// perform direct form I biquad calculation
y = o->b0 * x + o->b1 * o->x1 + o->b2 * o->x2 - o->a1 * o->y1 - o->a2 * o->y2;
// propagate the filter samples
o->x2 = o->x1;
o->x1 = x;
o->y2 = o->y1;
o->y1 = y;
// output the filtered sample
out[ n ] = y;
}
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 5 ] );
}
//------------------------------------------------------------------------------
// biquadratic_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void biquadratic_dsp( t_biquadratic* object, t_signal** sig )
{
object->sample_rate = sig[ 0 ]->s_sr;
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet1 sample vector
// inlet2 sample vector
// inlet3 sample vector
// inlet4 sample vector
// outlet sample vector
// sample frames to process (vector size)
// pointer to this object
dsp_add
(
biquadratic_perform,
7,
sig[ 0 ]->s_vec,
sig[ 1 ]->s_vec,
sig[ 2 ]->s_vec,
sig[ 3 ]->s_vec,
sig[ 4 ]->s_vec,
sig[ 0 ]->s_n,
object
);
}
//------------------------------------------------------------------------------
// biquadratic_arg_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void biquadratic_arg_dsp( t_biquadratic* object, t_signal** sig )
{
object->sample_rate = sig[ 0 ]->s_sr;
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet sample vector
// outlet sample vector
// sample frames to process (vector size)
// pointer to this object
dsp_add
(
biquadratic_arg_perform,
4,
sig[ 0 ]->s_vec,
sig[ 1 ]->s_vec,
sig[ 0 ]->s_n,
object
);
}
//------------------------------------------------------------------------------
// biquadratic_shape - sets filter shape based on name string
//------------------------------------------------------------------------------
static void biquadratic_shape( t_biquadratic* object, t_atom name )
{
const char* shape = name.a_w.w_symbol->s_name;
if ( StringMatch( shape, "lowpass" ) ){ object->shape = LOWPASS ; }
else if( StringMatch( shape, "highpass" ) ){ object->shape = HIGHPASS ; }
else if( StringMatch( shape, "bandpass" ) ){ object->shape = BANDPASS ; }
else if( StringMatch( shape, "allpass" ) ){ object->shape = ALLPASS ; }
else if( StringMatch( shape, "notch" ) ){ object->shape = NOTCH ; }
else if( StringMatch( shape, "peaking" ) ){ object->shape = PEAKING ; }
else if( StringMatch( shape, "lowshelf" ) ){ object->shape = LOWSHELF ; }
else if( StringMatch( shape, "highshelf" ) ){ object->shape = HIGHSHELF ; }
else
{
pd_error( object, "biquadratic~: unknown filter type" );
}
}
//------------------------------------------------------------------------------
// biquadratic_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* biquadratic_new( t_symbol* selector, t_int items, t_atom* list )
{
if( items > 1 )
{
// create a pointer to this object
t_biquadratic* object = ( t_biquadratic* )pd_new( biquadratic_arg_class );
// create a new signal outlet for this object
outlet_new( &object->object, gensym( "signal" ) );
// create float inlets
floatinlet_new( &object->object, &object->frequency );
floatinlet_new( &object->object, &object->quality );
floatinlet_new( &object->object, &object->decibels );
// initialize parameters
object->shape = LOWPASS;
object->frequency = 1000;
object->quality = 1;
object->decibels = 0;
// parse initialization arguments
//----------------------------------------------------------------------
if( items > 0 )
{
if( list[ 0 ].a_type == A_SYMBOL )
{
biquadratic_shape( object, list[ 0 ] );
}
else
{
pd_error( object, "biquadratic~: invalid argument 1 type" );
}
}
if( items > 1 )
{
if( list[ 1 ].a_type == A_FLOAT )
{
object->frequency = atom_getfloatarg( 1, ( int )items, list );
}
else
{
pd_error( object, "biquadratic~: invalid argument 2 type" );
}
}
if( items > 2 )
{
if( list[ 2 ].a_type == A_FLOAT )
{
object->quality = atom_getfloatarg( 2, ( int )items, list );
}
else
{
pd_error( object, "biquadratic~: invalid argument 3 type" );
}
}
if( items > 3 )
{
if( list[ 3 ].a_type == A_FLOAT )
{
object->decibels = atom_getfloatarg( 3, ( int )items, list );
}
else
{
pd_error( object, "biquadratic~: invalid argument 4 type" );
}
}
if( items > 4 )
{
pd_error( object, "biquadratic~: extra arguments ignored" );
}
return object;
}
else
{
// create a pointer to this object
t_biquadratic* object = ( t_biquadratic* )pd_new( biquadratic_class );
// create signal inlets
signalinlet_new( &object->object, object->frequency );
signalinlet_new( &object->object, object->quality );
signalinlet_new( &object->object, object->decibels );
// create a new signal outlet for this object
outlet_new( &object->object, gensym( "signal" ) );
// initialize parameters
object->shape = LOWPASS;
object->frequency = 1000;
object->quality = 1;
object->decibels = 0;
// set filter shape based on first argument
if( items > 0 )
{
if( list[ 0 ].a_type == A_SYMBOL )
{
biquadratic_shape( object, list[ 0 ] );
}
else
{
pd_error( object, "biquadratic~: invalid argument 1 type" );
}
}
return object;
}
}
//------------------------------------------------------------------------------
// biquadratic_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void biquadratic_tilde_setup( void )
{
// biquadratic class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
biquadratic_class = class_new( gensym( "biquadratic~" ), ( t_newmethod )biquadratic_new, 0, sizeof( t_biquadratic ), 0, A_GIMME, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( biquadratic_class, t_biquadratic, inlet_1 );
// installs biquadratic_dsp so that it will be called when dsp is turned on
class_addmethod( biquadratic_class, ( t_method )biquadratic_dsp, gensym( "dsp" ), 0 );
// install filter shape message handlers
class_addmethod( biquadratic_class, ( t_method )biquadratic_lowpass, gensym( "lowpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_highpass, gensym( "highpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_bandpass, gensym( "bandpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_allpass, gensym( "allpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_notch, gensym( "notch" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_peaking, gensym( "peaking" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_lowshelf, gensym( "lowshelf" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_highshelf, gensym( "highshelf" ), 0 );
// instal clear message handler
class_addmethod( biquadratic_class, ( t_method )biquadratic_clear, gensym( "clear" ), 0 );
// biquadratic arg class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
biquadratic_arg_class = class_new( gensym( "biquadratic~" ), 0, 0, sizeof( t_biquadratic ), 0, 0, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( biquadratic_arg_class, t_biquadratic, inlet_1 );
// installs biquadratic_dsp so that it will be called when dsp is turned on
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_arg_dsp, gensym( "dsp" ), 0 );
// install filter shape message handlers
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_lowpass, gensym( "lowpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_highpass, gensym( "highpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_bandpass, gensym( "bandpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_allpass, gensym( "allpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_notch, gensym( "notch" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_peaking, gensym( "peaking" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_lowshelf, gensym( "lowshelf" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_highshelf, gensym( "highshelf" ), 0 );
// instal clear message handler
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_clear, gensym( "clear" ), 0 );
// announce this object in the pd console
Announce( "biquadratic~: biquad filter - v1.0" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------
// Biquad Filter for Pd
//
// biquadratic~.c
//
// Filters signal through a general purpose biquad
//
// Created by Cooper on 10/16/15.
// Copyright (c) 2015 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
// todo
// add 'clear' message
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// utility header for Pd Spectral Toolkit project
#include "utility.h"
// included for float min
#include <limits.h>
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
//------------------------------------------------------------------------------
// biquadratic_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* biquadratic_class;
static t_class* biquadratic_arg_class;
//------------------------------------------------------------------------------
// filter_shape enum - filter shape names
//------------------------------------------------------------------------------
typedef enum
{
LOWPASS,
HIGHPASS,
BANDPASS,
NOTCH,
ALLPASS,
PEAKING,
LOWSHELF,
HIGHSHELF
}
filter_shape;
//------------------------------------------------------------------------------
// biquadratic - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct biquadratic
{
// this object - must always be first variable in struct
t_object object;
// needed for CLASS_MAINSIGNALIN macro call in biquadratic_tilde_setup
t_float inlet_1;
// floats to hold inlets' values
t_float frequency;
t_float quality;
t_float decibels;
// coefficients
double a1, a2, b0, b1, b2;
// filter sample memory
double x1, x2, y1, y2;
// the filter shape
filter_shape shape;
t_float sample_rate;
} t_biquadratic;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static void biquadratic_lowpass ( t_biquadratic* object );
static void biquadratic_highpass ( t_biquadratic* object );
static void biquadratic_bandpass ( t_biquadratic* object );
static void biquadratic_allpass ( t_biquadratic* object );
static void biquadratic_notch ( t_biquadratic* object );
static void biquadratic_peaking ( t_biquadratic* object );
static void biquadratic_lowshelf ( t_biquadratic* object );
static void biquadratic_highshelf ( t_biquadratic* object );
static void biquadratic_clear ( t_biquadratic* object );
static inline void biquadratic_calc ( t_biquadratic* object );
static t_int* biquadratic_perform ( t_int* io );
static t_int* biquadratic_arg_perform ( t_int* io );
static void biquadratic_dsp ( t_biquadratic* object, t_signal** sig );
static void biquadratic_arg_dsp ( t_biquadratic* object, t_signal** sig );
static void biquadratic_shape ( t_biquadratic* object, t_atom name );
static void* biquadratic_new ( t_symbol* selector, t_int items, t_atom* list );
void biquadratic_tilde_setup ( void );
//------------------------------------------------------------------------------
// biquadratic_lowpass - sets filter shape to lowpass
//------------------------------------------------------------------------------
static void biquadratic_lowpass( t_biquadratic* object )
{
object->shape = LOWPASS;
}
//------------------------------------------------------------------------------
// biquadratic_highpass - sets filter shape to highpass
//------------------------------------------------------------------------------
static void biquadratic_highpass( t_biquadratic* object )
{
object->shape = HIGHPASS;
}
//------------------------------------------------------------------------------
// biquadratic_bandpass - sets filter shape to bandpass
//------------------------------------------------------------------------------
static void biquadratic_bandpass( t_biquadratic* object )
{
object->shape = BANDPASS;
}
//------------------------------------------------------------------------------
// biquadratic_allpass - sets filter shape to allpass
//------------------------------------------------------------------------------
static void biquadratic_allpass( t_biquadratic* object )
{
object->shape = ALLPASS;
}
//------------------------------------------------------------------------------
// biquadratic_notch - sets filter shape to notch
//------------------------------------------------------------------------------
static void biquadratic_notch( t_biquadratic* object )
{
object->shape = NOTCH;
}
//------------------------------------------------------------------------------
// biquadratic_peaking - sets filter shape to peaking
//------------------------------------------------------------------------------
static void biquadratic_peaking( t_biquadratic* object )
{
object->shape = PEAKING;
}
//------------------------------------------------------------------------------
// biquadratic_lowshelf - sets filter shape to lowshelf
//------------------------------------------------------------------------------
static void biquadratic_lowshelf( t_biquadratic* object )
{
object->shape = LOWSHELF;
}
//------------------------------------------------------------------------------
// biquadratic_highshelf - sets filter shape to highshelf
//------------------------------------------------------------------------------
static void biquadratic_highshelf( t_biquadratic* object )
{
object->shape = HIGHSHELF;
}
//------------------------------------------------------------------------------
// biquadratic_clear - clears filter memory
//------------------------------------------------------------------------------
static void biquadratic_clear( t_biquadratic* object )
{
object->x1 = 0;
object->x2 = 0;
object->y1 = 0;
object->y2 = 0;
}
//------------------------------------------------------------------------------
// biquadratic_calc - calculates the filter coefficients
//
// Adapted From:
// Cookbook formulae for audio EQ biquad filter coefficients
// By Robert Bristow-Johnson
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
//
//------------------------------------------------------------------------------
static inline void biquadratic_calc( t_biquadratic* object )
{
// intermediate variables
double quality = ClipMin( object->quality, C_FLOAT_MIN );
double omega = C_2_PI * Clip( object->frequency, C_FLOAT_MIN, object->sample_rate * 0.5 ) / object->sample_rate;
double cos_omega = Cosine( omega );
double alpha = Sine( omega ) / ( 2.0 * quality );
double amp, beta;
double a0, a1, a2, b0, b1, b2;
// calculate based on filter shape
switch( object->shape )
{
case LOWPASS : b0 = ( 1.0 - cos_omega ) * 0.5 ;
b1 = 1.0 - cos_omega ;
b2 = ( 1.0 - cos_omega ) * 0.5 ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case HIGHPASS : b0 = ( 1.0 + cos_omega ) * 0.5 ;
b1 = -( 1.0 + cos_omega ) ;
b2 = ( 1.0 + cos_omega ) * 0.5 ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case BANDPASS : b0 = alpha * quality ;
b1 = 0.0 ;
b2 = -alpha * quality ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case ALLPASS : b0 = 1.0 - alpha ;
b1 = -2.0 * cos_omega ;
b2 = 1.0 + alpha ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case NOTCH : b0 = 1.0 ;
b1 = -2.0 * cos_omega ;
b2 = 1.0 ;
a0 = 1.0 + alpha ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - alpha ;
break;
case PEAKING : amp = SquareRoot( Power( 10.0, ( object->decibels * 0.05 ) ) );
b0 = 1.0 + ( alpha * amp ) ;
b1 = -2.0 * cos_omega ;
b2 = 1.0 - ( alpha * amp ) ;
a0 = 1.0 + ( alpha / amp ) ;
a1 = -2.0 * cos_omega ;
a2 = 1.0 - ( alpha / amp ) ;
break;
case LOWSHELF : amp = SquareRoot( Power( 10.0, ( object->decibels * 0.05 ) ) );
beta = 2.0 * SquareRoot( amp ) * alpha;
b0 = amp * ( ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega + beta ) ;
b1 = 2.0 * amp * ( ( amp - 1.0 ) - ( amp + 1.0 ) * cos_omega ) ;
b2 = amp * ( ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega - beta ) ;
a0 = ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega + beta ;
a1 = -2.0 * ( ( amp - 1.0 ) + ( amp + 1.0 ) * cos_omega ) ;
a2 = ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega - beta ;
break;
case HIGHSHELF : amp = SquareRoot( Power( 10.0, ( object->decibels * 0.05 ) ) );
beta = 2.0 * SquareRoot( amp ) * alpha;
b0 = amp * ( ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega + beta ) ;
b1 = -2.0 * amp * ( ( amp - 1.0 ) + ( amp + 1.0 ) * cos_omega ) ;
b2 = amp * ( ( amp + 1.0 ) + ( amp - 1.0 ) * cos_omega - beta ) ;
a0 = ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega + beta ;
a1 = 2.0 * ( ( amp - 1.0 ) - ( amp + 1.0 ) * cos_omega ) ;
a2 = ( amp + 1.0 ) - ( amp - 1.0 ) * cos_omega - beta ;
break;
}
// normalize coefficients
object->b0 = b0 / a0;
object->b1 = b1 / a0;
object->b2 = b2 / a0;
object->a1 = a1 / a0;
object->a2 = a2 / a0;
}
//------------------------------------------------------------------------------
// biquadratic_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* biquadratic_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in1 = ( t_float* )( io[ 1 ] );
t_float* in2 = ( t_float* )( io[ 2 ] );
t_float* in3 = ( t_float* )( io[ 3 ] );
t_float* in4 = ( t_float* )( io[ 4 ] );
t_float* out = ( t_float* )( io[ 5 ] );
t_int frames = ( t_int )( io[ 6 ] );
t_biquadratic* o = ( t_biquadratic* )( io[ 7 ] );
// sample i/o temp
double x, y;
// signal vector iterator variable
t_int n = -1;
// the dsp loop
while( ++n < frames )
{
// store the input sample
x = in1[ n ];
// store the parameters
o->frequency = in2[ n ];
o->quality = in3[ n ];
o->decibels = in4[ n ];
// calculate coefficients
biquadratic_calc( o );
// perform direct form I biquad calculation
y = o->b0 * x + o->b1 * o->x1 + o->b2 * o->x2 - o->a1 * o->y1 - o->a2 * o->y2;
// propagate the filter samples
o->x2 = o->x1;
o->x1 = x;
o->y2 = o->y1;
o->y1 = y;
// output the filtered sample
out[ n ] = y;
}
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 8 ] );
}
//------------------------------------------------------------------------------
// biquadratic_arg_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* biquadratic_arg_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in = ( t_float* )( io[ 1 ] );
t_float* out = ( t_float* )( io[ 2 ] );
t_int frames = ( t_int )( io[ 3 ] );
t_biquadratic* o = ( t_biquadratic* )( io[ 4 ] );
// signal vector iterator variable
t_int n = -1;
// sample i/o temp
double x, y;
// calculate coefficients
biquadratic_calc( o );
// the dsp loop
while( ++n < frames )
{
// store the input sample
x = in[ n ];
// perform direct form I biquad calculation
y = o->b0 * x + o->b1 * o->x1 + o->b2 * o->x2 - o->a1 * o->y1 - o->a2 * o->y2;
// propagate the filter samples
o->x2 = o->x1;
o->x1 = x;
o->y2 = o->y1;
o->y1 = y;
// output the filtered sample
out[ n ] = y;
}
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 5 ] );
}
//------------------------------------------------------------------------------
// biquadratic_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void biquadratic_dsp( t_biquadratic* object, t_signal** sig )
{
object->sample_rate = sig[ 0 ]->s_sr;
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet1 sample vector
// inlet2 sample vector
// inlet3 sample vector
// inlet4 sample vector
// outlet sample vector
// sample frames to process (vector size)
// pointer to this object
dsp_add
(
biquadratic_perform,
7,
sig[ 0 ]->s_vec,
sig[ 1 ]->s_vec,
sig[ 2 ]->s_vec,
sig[ 3 ]->s_vec,
sig[ 4 ]->s_vec,
sig[ 0 ]->s_n,
object
);
}
//------------------------------------------------------------------------------
// biquadratic_arg_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void biquadratic_arg_dsp( t_biquadratic* object, t_signal** sig )
{
object->sample_rate = sig[ 0 ]->s_sr;
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet sample vector
// outlet sample vector
// sample frames to process (vector size)
// pointer to this object
dsp_add
(
biquadratic_arg_perform,
4,
sig[ 0 ]->s_vec,
sig[ 1 ]->s_vec,
sig[ 0 ]->s_n,
object
);
}
//------------------------------------------------------------------------------
// biquadratic_shape - sets filter shape based on name string
//------------------------------------------------------------------------------
static void biquadratic_shape( t_biquadratic* object, t_atom name )
{
const char* shape = name.a_w.w_symbol->s_name;
if ( StringMatch( shape, "lowpass" ) ){ object->shape = LOWPASS ; }
else if( StringMatch( shape, "highpass" ) ){ object->shape = HIGHPASS ; }
else if( StringMatch( shape, "bandpass" ) ){ object->shape = BANDPASS ; }
else if( StringMatch( shape, "allpass" ) ){ object->shape = ALLPASS ; }
else if( StringMatch( shape, "notch" ) ){ object->shape = NOTCH ; }
else if( StringMatch( shape, "peaking" ) ){ object->shape = PEAKING ; }
else if( StringMatch( shape, "lowshelf" ) ){ object->shape = LOWSHELF ; }
else if( StringMatch( shape, "highshelf" ) ){ object->shape = HIGHSHELF ; }
else
{
pd_error( object, "biquadratic~: unknown filter type" );
}
}
//------------------------------------------------------------------------------
// biquadratic_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* biquadratic_new( t_symbol* selector, t_int items, t_atom* list )
{
if( items > 1 )
{
// create a pointer to this object
t_biquadratic* object = ( t_biquadratic* )pd_new( biquadratic_arg_class );
// create a new signal outlet for this object
outlet_new( &object->object, gensym( "signal" ) );
// create float inlets
floatinlet_new( &object->object, &object->frequency );
floatinlet_new( &object->object, &object->quality );
floatinlet_new( &object->object, &object->decibels );
// initialize parameters
object->shape = LOWPASS;
object->frequency = 1000;
object->quality = 1;
object->decibels = 0;
// parse initialization arguments
//----------------------------------------------------------------------
if( items > 0 )
{
if( list[ 0 ].a_type == A_SYMBOL )
{
biquadratic_shape( object, list[ 0 ] );
}
else
{
pd_error( object, "biquadratic~: invalid argument 1 type" );
}
}
if( items > 1 )
{
if( list[ 1 ].a_type == A_FLOAT )
{
object->frequency = atom_getfloatarg( 1, ( int )items, list );
}
else
{
pd_error( object, "biquadratic~: invalid argument 2 type" );
}
}
if( items > 2 )
{
if( list[ 2 ].a_type == A_FLOAT )
{
object->quality = atom_getfloatarg( 2, ( int )items, list );
}
else
{
pd_error( object, "biquadratic~: invalid argument 3 type" );
}
}
if( items > 3 )
{
if( list[ 3 ].a_type == A_FLOAT )
{
object->decibels = atom_getfloatarg( 3, ( int )items, list );
}
else
{
pd_error( object, "biquadratic~: invalid argument 4 type" );
}
}
if( items > 4 )
{
pd_error( object, "biquadratic~: extra arguments ignored" );
}
return object;
}
else
{
// create a pointer to this object
t_biquadratic* object = ( t_biquadratic* )pd_new( biquadratic_class );
// create signal inlets
signalinlet_new( &object->object, object->frequency );
signalinlet_new( &object->object, object->quality );
signalinlet_new( &object->object, object->decibels );
// create a new signal outlet for this object
outlet_new( &object->object, gensym( "signal" ) );
// initialize parameters
object->shape = LOWPASS;
object->frequency = 1000;
object->quality = 1;
object->decibels = 0;
// set filter shape based on first argument
if( items > 0 )
{
if( list[ 0 ].a_type == A_SYMBOL )
{
biquadratic_shape( object, list[ 0 ] );
}
else
{
pd_error( object, "biquadratic~: invalid argument 1 type" );
}
}
return object;
}
}
//------------------------------------------------------------------------------
// biquadratic_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void biquadratic_tilde_setup( void )
{
// biquadratic class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
biquadratic_class = class_new( gensym( "biquadratic~" ), ( t_newmethod )biquadratic_new, 0, sizeof( t_biquadratic ), 0, A_GIMME, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( biquadratic_class, t_biquadratic, inlet_1 );
// installs biquadratic_dsp so that it will be called when dsp is turned on
class_addmethod( biquadratic_class, ( t_method )biquadratic_dsp, gensym( "dsp" ), 0 );
// install filter shape message handlers
class_addmethod( biquadratic_class, ( t_method )biquadratic_lowpass, gensym( "lowpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_highpass, gensym( "highpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_bandpass, gensym( "bandpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_allpass, gensym( "allpass" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_notch, gensym( "notch" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_peaking, gensym( "peaking" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_lowshelf, gensym( "lowshelf" ), 0 );
class_addmethod( biquadratic_class, ( t_method )biquadratic_highshelf, gensym( "highshelf" ), 0 );
// instal clear message handler
class_addmethod( biquadratic_class, ( t_method )biquadratic_clear, gensym( "clear" ), 0 );
// biquadratic arg class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
biquadratic_arg_class = class_new( gensym( "biquadratic~" ), 0, 0, sizeof( t_biquadratic ), 0, 0, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( biquadratic_arg_class, t_biquadratic, inlet_1 );
// installs biquadratic_dsp so that it will be called when dsp is turned on
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_arg_dsp, gensym( "dsp" ), 0 );
// install filter shape message handlers
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_lowpass, gensym( "lowpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_highpass, gensym( "highpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_bandpass, gensym( "bandpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_allpass, gensym( "allpass" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_notch, gensym( "notch" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_peaking, gensym( "peaking" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_lowshelf, gensym( "lowshelf" ), 0 );
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_highshelf, gensym( "highshelf" ), 0 );
// instal clear message handler
class_addmethod( biquadratic_arg_class, ( t_method )biquadratic_clear, gensym( "clear" ), 0 );
// announce this object in the pd console
Announce( "biquadratic~: biquad filter - v1.0" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------